//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioControls.WorkflowManagerModule
{
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Input;
    using ManagementStudioServices.Interfaces;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
    using Microsoft.Research.DataLayer;
    using System.Linq;
    using System.Collections.Generic;
    using Microsoft.Practices.Unity;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioModels;
    using Microsoft.Research.ScientificWorkflow.TridentComposer;

    /// <summary>
    /// The Presentation logic for the schedule window.
    /// </summary>
    public class SchedulerViewPresenter : DependencyObject, INotifyPropertyChanged
    {
        #region private memebers

        /// <summary>
        /// The unity container instance.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// The workflow manager service instance.
        /// </summary>
        private IWorkflowManagerService workflowManagerService;

        /// <summary>
        /// Schedule windows reference.
        /// </summary>
        private SchedulerView view;

        /// <summary>
        /// The ID of the workflow selected.
        /// </summary>
        private Guid workflowId;

        /// <summary>
        /// The ID of the schedule selected.
        /// </summary>
        private Guid scheduleId;

        /// <summary>
        /// The selected node.
        /// </summary>
        private Machine selectedNode;

        /// <summary>
        /// JobSchedule reference.
        /// </summary>
        private JobSchedule scheduleToRegistry;

        /// <summary>
        /// The selected user.
        /// </summary>
        private User selectedUser;

        /// <summary>
        /// Schedule Mode : Edit\New\Delete.
        /// </summary>
        private ScheduleMode scheduleMode;

        /// <summary>
        /// The job name.
        /// </summary>
        private string jobName = string.Empty;

        /// <summary>
        /// If current schedule is having an expiry date.
        /// </summary>
        private bool isExpireEnabled;

        /// <summary>
        /// Is this instance having valid data.
        /// </summary>
        private bool isValidData = true;

        /// <summary>
        /// Refreshes the Nodes list.
        /// </summary>
        public DelegateCommand<bool> RefreshNodesCommand
        {
            get;
            set;
        }

        #region Recurrence variables

        /// <summary>
        /// Recur one time.
        /// </summary>
        private bool onetimeRecurrence = true;

        /// <summary>
        /// Recur daily.
        /// </summary>
        private bool dailyRecurrence;

        /// <summary>
        /// Recur weekly.
        /// </summary>
        private bool weeklyRecurrence;

        /// <summary>
        /// Frequency in daily scheduled.
        /// </summary>
        private int recurInDays = 1;

        /// <summary>
        /// Frequency in weekly scheduled.
        /// </summary>
        private int recurInWeeks = 1;

        #endregion

        #region Week Days variables
        /// <summary>
        /// Sunday.
        /// </summary>
        private bool isSunday;

        /// <summary>
        /// Monday.
        /// </summary>
        private bool isMonday;

        /// <summary>
        /// Tuseday.
        /// </summary>
        private bool isTuesday;

        /// <summary>
        /// Wednesday.
        /// </summary>
        private bool isWednesday;

        /// <summary>
        /// Thursday.
        /// </summary>
        private bool isThursday;

        /// <summary>
        /// Friday.
        /// </summary>
        private bool isFriday;

        /// <summary>
        /// Saturday.
        /// </summary>
        private bool isSaturday;

        #endregion

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="SchedulerViewPresenter"/> class.
        /// </summary>
        /// <param name="container">The unity container.</param>
        /// <param name="passedId">The passed id.</param>
        /// <param name="scheduleMode">The schedule mode.</param>
        /// <param name="view">Scheduler view.</param>
        /// <param name="service">workflow manager service.</param>
        public SchedulerViewPresenter(IUnityContainer container, Guid instanceId, ScheduleMode scheduleMode, SchedulerView view, IWorkflowManagerService service)
        {
            this.NodeList = new ObservableCollection<Machine>();
            this.container = container;

            // Get the schedule mode.
            this.scheduleMode = scheduleMode;

            // Set the Schedule window dataContext.
            view.DataContext = this;

            this.view = view;
            this.workflowManagerService = service;




            if (this.scheduleMode == ScheduleMode.Edit)
            {
                this.scheduleId = instanceId;
            }
            else if (this.scheduleMode == ScheduleMode.New)
            {
                this.workflowId = instanceId;
            }

            this.AdditionalParameters = new ObservableCollection<AdditionalParametersModel>();

            // Initialize presenter.
            this.Intialize();
            this.WorkflowName = this.WorkflowModel.GetDisplayName;

            // Assign the commands.
            this.SaveScheduleCommand = new DelegateCommand<object>(p => this.ExecuteSaveScheduleCommand());
            this.CloseScheduleWindowCommand = new DelegateCommand<object>(p => this.ExecuteCloseWindow());
            this.AddAdditionalParameterCommand = new DelegateCommand<object>(p => AddAdditionalParametersExecute());
            this.DeleteAdditionalParameterCommand =
                new DelegateCommand<AdditionalParametersModel>(this.DeleteAdditionalParametersExecute);

            this.RefreshNodesCommand = new DelegateCommand<bool>(ExecuteRefreshNodesCommand);
            // Initialize the scheduler window.
            this.view.Owner = Application.Current.MainWindow;
            this.view.Initialize(this.WorkflowModel, this.container.Resolve<Dictionary<string, DataTypeModel>>("Datatypes"));

            // Get available users and machines.
            this.ExecuteRefreshNodesCommand(true);
            this.GetUserList();
            // Set the current user as the default scheduler.
            this.SelectDefaultUser();

            this.view.ShowDialog();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the name of the workflow.
        /// </summary>
        /// <value>The name of the workflow.</value>
        public string WorkflowName { get; private set; }

        /// <summary>
        /// Gets or sets the selected days.
        /// </summary>
        /// <value>The selected days.</value>
        public int SelectedDays { get; set; }

        /// <summary>
        /// Gets or sets the name of the job.
        /// </summary>
        /// <value>The name of the job.</value>
        public string JobName
        {
            get
            {
                return this.jobName;
            }
            set
            {
                this.jobName = (!string.IsNullOrEmpty(value)) ?
                    value :
                    this.WorkflowModel.Name + " - " + DateTime.Now.ToString();
                this.OnNotifyPropertyChanged("JobName");
            }
        }

        /// <summary>
        /// Gets the list of nodes in the registry.
        /// </summary>
        /// <value>The node list.</value>
        public ObservableCollection<Machine> NodeList { get; private set; }

        /// <summary>
        /// Gets the save schedule command.
        /// </summary>
        /// <value>The save schedule command.</value>
        public ICommand SaveScheduleCommand { get; private set; }

        /// <summary>
        /// Gets the close schedule window command.
        /// </summary>
        /// <value>The close schedule window command.</value>
        public ICommand CloseScheduleWindowCommand { get; private set; }
        
        /// <summary>
        /// Command to add a additional parameter.
        /// </summary>
        public ICommand AddAdditionalParameterCommand { get; private set; }

        /// <summary>
        /// Command to remove an additional parameter.
        /// </summary>
        public DelegateCommand<AdditionalParametersModel> DeleteAdditionalParameterCommand { get; private set; }

        /// <summary>
        /// Gets the model of the workflow to be displayed in the window.
        /// </summary>
        /// <value>The workflow model.</value>
        public TridentWorkflowModel WorkflowModel { get; private set; }

        /// <summary>
        /// Gets the list of users.
        /// </summary>
        /// <value>The user list.</value>
        public Collection<User> UserList { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is schedule registry modified.
        /// Exposed for management studio.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is schedule registry modified; otherwise, <c>false</c>.
        /// </value>
        public bool IsScheduleRegistryModified { get; set; }

        /// <summary>
        /// Gets or sets recurrence.for days or weeks 
        /// </summary>
        /// <value>The recurrence.</value>
        public int Recurrence { get; set; }

        /// <summary>
        /// Gets or sets the schedule trigger : OneTime\Daily\Weekly.
        /// </summary>
        /// <value>The type of the schedule trigger.</value>
        public ScheduleType ScheduleTriggerType { get; set; }

        /// <summary>
        /// Gets or sets the name of schedule.
        /// </summary>
        /// <value>Name of the schedule.</value>
        public string ScheduleName { get; set; }

        /// <summary>
        /// Gets or sets the notes for the schedule.
        /// </summary>
        public string Notes { get; set; }

        /// <summary>
        /// Gets the additional parameters collection.
        /// </summary>
        public ObservableCollection<AdditionalParametersModel> AdditionalParameters { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is expire enabled.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is expire enabled; otherwise, <c>false</c>.
        /// </value>
        public bool IsExpireEnabled
        {
            get
            {
                return this.isExpireEnabled;
            }
            set
            {
                this.isExpireEnabled = value;
                this.OnNotifyPropertyChanged("IsExpireEnabled");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance has data valid.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has data valid; otherwise, <c>false</c>.
        /// </value>
        public bool IsValidData
        {
            get
            {
                return this.isValidData;
            }
            set
            {
                this.isValidData = value;
                this.OnNotifyPropertyChanged("IsValidData");
            }
        }
        
        #region Recurrence Properties

        /// <summary>
        /// Gets or sets a value indicating whether [onetime recurrence].
        /// </summary>
        /// <value><c>true</c> if [onetime recurrence]; otherwise, <c>false</c>.</value>
        public bool OnetimeRecurrence
        {
            get
            {
                return this.onetimeRecurrence;
            }
            set
            {
                this.onetimeRecurrence = value;

                if (value)
                {
                    this.WeeklyRecurrence = false;
                    this.DailyRecurrence = false;
                }

                ValidateSeletctedWeekdays();
                this.OnNotifyPropertyChanged("OnetimeRecurrence");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [daily recurrence].
        /// </summary>
        /// <value><c>true</c> if [daily recurrence]; otherwise, <c>false</c>.</value>
        public bool DailyRecurrence
        {
            get
            {
                return this.dailyRecurrence;
            }
            set
            {
                this.dailyRecurrence = value;

                if (value)
                {
                    this.OnetimeRecurrence = false;
                    this.WeeklyRecurrence = false;
                }

                ValidateSeletctedWeekdays();
                this.OnNotifyPropertyChanged("DailyRecurrence");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [weekly recurrence].
        /// </summary>
        /// <value><c>true</c> if [weekly recurrence]; otherwise, <c>false</c>.</value>
        public bool WeeklyRecurrence
        {
            get
            {

                return this.weeklyRecurrence;
            }
            set
            {
                this.weeklyRecurrence = value;

                if (value)
                {
                    this.OnetimeRecurrence = false;
                    this.DailyRecurrence = false;
                }

                ValidateSeletctedWeekdays();
                this.OnNotifyPropertyChanged("WeeklyRecurrence");
            }
        }

        /// <summary>
        /// Gets or sets the recur in days.
        /// </summary>
        /// <value>The recur in days.</value>
        public int RecurInDays
        {
            get
            {
                return this.recurInDays;
            }
            set
            {
                if (value <= 0)
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(
                        ManagementStudioResourceManager.GetString("SchedulerDaysRecurError"));
                    this.recurInDays = 1;
                }
                else
                {
                    this.recurInDays = value;
                }

                this.OnNotifyPropertyChanged("RecurInDays");
            }
        }

        /// <summary>
        /// Gets or sets the recur in weeks.
        /// </summary>
        /// <value>The recur in weeks.</value>
        public int RecurInWeeks
        {
            get
            {
                return this.recurInWeeks;
            }
            set
            {
                if (value <= 0 || value > 52)
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(
                        ManagementStudioResourceManager.GetString("SchedulerWeekRecurError"));
                    this.recurInWeeks = 1;
                }
                else
                {
                    this.recurInWeeks = value;
                } this.OnNotifyPropertyChanged("RecurInWeeks");
            }
        }

        #endregion

        #region WeekDays Properties
        /// <summary>
        /// Gets or sets a value indicating whether this instance is sunday.
        /// </summary>
        /// <value><c>true</c> if this instance is sunday; otherwise, <c>false</c>.</value>
        public bool IsSunday
        {
            get
            {
                return this.isSunday;
            }
            set
            {
                this.isSunday = value;
                this.ValidateSeletctedWeekdays();
                this.OnNotifyPropertyChanged("IsSunday");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is monday.
        /// </summary>
        /// <value><c>true</c> if this instance is monday; otherwise, <c>false</c>.</value>
        public bool IsMonday
        {
            get
            {
                return this.isMonday;
            }
            set
            {
                this.isMonday = value;
                this.ValidateSeletctedWeekdays();
                this.OnNotifyPropertyChanged("IsMonday");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is tuesday.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is tuesday; otherwise, <c>false</c>.
        /// </value>
        public bool IsTuesday
        {
            get
            {
                return this.isTuesday;
            }
            set
            {
                this.isTuesday = value;
                this.ValidateSeletctedWeekdays();
                this.OnNotifyPropertyChanged("IsTuesday");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is wednesday.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is wednesday; otherwise, <c>false</c>.
        /// </value>
        public bool IsWednesday
        {
            get
            {
                return this.isWednesday;
            }
            set
            {
                this.isWednesday = value;
                this.ValidateSeletctedWeekdays();
                this.OnNotifyPropertyChanged("IsWednesday");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is thursday.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is thursday; otherwise, <c>false</c>.
        /// </value>
        public bool IsThursday
        {
            get
            {
                return this.isThursday;
            }
            set
            {
                this.isThursday = value;
                this.ValidateSeletctedWeekdays();
                this.OnNotifyPropertyChanged("IsThursday");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is friday.
        /// </summary>
        /// <value><c>true</c> if this instance is friday; otherwise, <c>false</c>.</value>
        public bool IsFriday
        {
            get
            {
                return this.isFriday;
            }
            set
            {
                this.isFriday = value;
                this.ValidateSeletctedWeekdays();
                this.OnNotifyPropertyChanged("IsFriday");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is saturday.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is saturday; otherwise, <c>false</c>.
        /// </value>
        public bool IsSaturday
        {
            get
            {
                return this.isSaturday;
            }
            set
            {
                this.isSaturday = value;
                this.ValidateSeletctedWeekdays();
                this.OnNotifyPropertyChanged("IsSaturday");
            }
        }

        #endregion

        /// <summary>
        /// Gets or sets the start schedule time.
        /// </summary>
        /// <value>The start schedule time.</value>
        public DateTime StartScheduleTime
        {
            get
            {
                return this.view.StartDateTime;
            }

            set
            {
                this.view.StartDateTime = value;
            }
        }

        /// <summary>
        /// Gets or sets the end schedule time.
        /// </summary>
        /// <value>The end schedule time.</value>
        public DateTime EndScheduleTime
        {
            get
            {
                return this.view.EndDateTime;
            }

            set
            {
                this.view.EndDateTime = value;
            }
        }

        /// <summary>
        /// Gets or sets the selected machine.
        /// </summary>
        /// <value>The selected machine.</value>
        public Machine SelectedMachine
        {
            get
            {
                return this.selectedNode;
            }

            set
            {
                this.selectedNode = value;
                this.OnNotifyPropertyChanged("SelectedMachine");
            }
        }

        /// <summary>
        /// Gets or sets the selected user.
        /// </summary>
        /// <value>The selected user.</value>
        public User SelectedUser
        {
            get
            {
                return this.selectedUser;
            }

            set
            {
                this.selectedUser = value;
                this.OnNotifyPropertyChanged("SelectedUser");
            }
        }

        /// <summary>
        /// Gets or sets the workflow.
        /// </summary>
        /// <value>The workflow.</value>
        public Activity Workflow { get; set; }

        /// <summary>
        /// Flag to check if all the properties have valid values in the property pane
        /// </summary>
        public static readonly DependencyProperty PropertiesValidProperty =
        DependencyProperty.Register("PropertiesValid", typeof(Boolean), typeof(SchedulerViewPresenter), new PropertyMetadata(true));

        /// <summary>
        /// Flag to check if all the properties have valid values in the property pane.
        /// </summary>
        public bool PropertiesValid
        {
            get { return (bool)GetValue(PropertiesValidProperty); }
            set { SetValue(PropertiesValidProperty, value); }
        }

        #endregion

        #region Member Methods

        /// <summary>
        /// Intializes this instance.
        /// </summary>
        private void Intialize()
        {
            // Check for new or edit schedule.
            if (this.scheduleMode == ScheduleMode.Edit)
            {
                this.ScheduleEditModeInitilization();
            }
            else if (this.scheduleMode == ScheduleMode.New)
            {
                this.ScheduleNewModeInitialization();
            }
        }

        /// <summary>
        /// Initialize schedule in new mode.
        /// </summary>
        private void ScheduleNewModeInitialization()
        {
            // Set the selected item in the combobox.
            if (this.NodeList.Count > 0)
            {
                this.SelectedMachine = this.NodeList[0];
            }

            this.StartScheduleTime = DateTime.Now;
            this.EndScheduleTime = DateTime.Now.AddYears(1);

            // Get the model and the node list.
            this.CreateWorkflowModel();
        }

        /// <summary>
        /// Initialize schedule in edits mode.
        /// </summary>
        private void ScheduleEditModeInitilization()
        {
            try
            {
                // Get the schedule to edit it.
                JobSchedule selectedSchedule =
                    this.workflowManagerService.GetSchedule(this.scheduleId);

                // Get the associated job with this schedule.
                Job job = selectedSchedule.Template;

                // Get the associated workflow.
                this.workflowId = (job.ActivityInstances[0].Activity as IObject).ID;
                this.JobName = selectedSchedule.Name;

                // Get the schedule expiration data.
                this.IsExpireEnabled = selectedSchedule.IsExpireEnable;

                // Set the notes.
                this.Notes = job.Notes;

                //Get the schedule start and end time.
                this.StartScheduleTime = selectedSchedule.StartTime;
                this.EndScheduleTime = selectedSchedule.StopTime;

                // Get the selected machine and user.
                this.SelectedMachine = job.Machines[0];
                this.SelectedUser = job.ScheduledBy;

                // Get the additional parameters.
                foreach (AdditionalParameter additionalParameter in job.AdditionalParameters)
                {
                    this.AdditionalParameters.Add(new AdditionalParametersModel(additionalParameter));
                }

                // Get the schedule type and recurrence.
                if (selectedSchedule.Schedule == ScheduleType.OneTime)
                {
                    this.OnetimeRecurrence = true;
                }
                else if (selectedSchedule.Schedule == ScheduleType.Daily)
                {
                    this.DailyRecurrence = true;
                    this.RecurInDays = selectedSchedule.Recurrence;
                }
                else if (selectedSchedule.Schedule == ScheduleType.Weekly)
                {
                    this.WeeklyRecurrence = true;
                    this.RecurInWeeks = selectedSchedule.Recurrence;
                    this.SelectedDays = selectedSchedule.Days;

                    // Check the selected dayes.
                    if (((int)DaysOfWeeks.Sunday & this.SelectedDays) != 0)
                    {
                        this.IsSunday = true;
                    }

                    if (((int)DaysOfWeeks.Monday & this.SelectedDays) != 0)
                    {
                        this.IsMonday = true;
                    }

                    if (((int)DaysOfWeeks.Tuesday & this.SelectedDays) != 0)
                    {
                        this.IsTuesday = true;
                    }

                    if (((int)DaysOfWeeks.Wednesday & this.SelectedDays) != 0)
                    {
                        this.IsWednesday = true;
                    }

                    if (((int)DaysOfWeeks.Thursday & this.SelectedDays) != 0)
                    {
                        this.IsThursday = true;
                    }

                    if (((int)DaysOfWeeks.Friday & this.SelectedDays) != 0)
                    {
                        this.IsFriday = true;
                    }

                    if (((int)DaysOfWeeks.Saturday & this.SelectedDays) != 0)
                    {
                        this.IsSaturday = true;
                    }
                }

                // Get the model and the node list.
                this.CreateWorkflowModel();

                // Update the workflow model to get the default properties.
                this.UpdateWorkflowModel(job.ActivityInstances[0].ActivityInstanceParameters);
            }
            catch (Exception exception)
            {
                TridentErrorHandler.ProcessKnownExceptionsInBusinessLogic(exception);
                TridentErrorHandler.ThrowTridentCustomException(
                    TridentErrorConstants.ErrorNumber1000000023,
                    ManagementStudioResourceManager.GetString("ScheduleSaveError"));
            }
        }

        /// <summary>
        /// Executes the create schedule in registry command.
        /// </summary>
        private void ExecuteSaveScheduleCommand()
        {
            // Save the schedule.
            this.SaveScheduleInRegistry();
        }

        /// <summary>
        /// Close the window.
        /// </summary>
        private void ExecuteCloseWindow()
        {
            // Close the schedule view.
            this.view.DataContext = null;
            this.view.Close();
        }


        /// <summary>
        /// Add an additional parameter to the list.
        /// </summary>
        private void AddAdditionalParametersExecute()
        {
            this.AdditionalParameters.Add(new AdditionalParametersModel());
        }

        /// <summary>
        /// Remove an additional parameter in the list.
        /// </summary>
        /// <param name="model">The additional parameter to delete.</param>
        private void DeleteAdditionalParametersExecute(AdditionalParametersModel model)
        {
            this.AdditionalParameters.Remove(model);
        }

        /// <summary>
        /// Retrieve the list of nodes present.
        /// </summary>
        private void ExecuteRefreshNodesCommand(bool selectDefaultNode)
        {
            Machine defaultNode = null;
            bool lastSelectedNodeExist = false;
            Machine lastSelectedMachine = this.SelectedMachine;
            this.NodeList.Clear();
            Collection<Machine> machines =
                this.workflowManagerService.GetAllMachines(this.WorkflowModel.IsInteractive);
            
            if (machines!= null && machines.Count > 0)
            {
                foreach (Machine mac in machines)
                {
                    this.NodeList.Add(mac);
                    if (mac.Name.Equals(Environment.MachineName, StringComparison.OrdinalIgnoreCase))
                    {
                        //this.SelectedMachine = mac;
                        defaultNode = mac;
                    }
                    //If last selected machine exist in the new node collection
                    if ((lastSelectedMachine != null) && (mac.Name.Equals(lastSelectedMachine.Name, StringComparison.OrdinalIgnoreCase)))
                    {
                        lastSelectedNodeExist = true;
                    }
                }
                //If default value to be set, check if the Executing machine name is available in the machines list
                //if available select it, else select the first node
                if (selectDefaultNode)
                {
                    this.SelectedMachine = defaultNode;
                }
                else
                {
                    //Set the previously selected value
                    if (lastSelectedNodeExist)
                    {
                        this.SelectedMachine = lastSelectedMachine;
                    }
                    else
                    {
                        this.SelectedMachine = null;
                    }
                }
            }
        }

        /// <summary>
        /// Retrieve the list of Users present.
        /// </summary>
        private void GetUserList()
        {
            this.UserList = this.workflowManagerService.GetAllAuthenticatedUsers();
        }

        /// <summary>
        /// Select the current user as the default user.
        /// </summary>
        private void SelectDefaultUser()
        {
            string userName = TridentAuthentication.LoggedUserInUserName;//Environment.UserName;

            if (this.UserList != null && this.UserList.Count > 0)
            {
                this.SelectedUser = this.UserList.First(user => user.Name.Equals(userName, StringComparison.OrdinalIgnoreCase));

                if (this.SelectedUser == null)
                {
                    // If the current user is missing in the user list then select the first in the list.
                    this.SelectedUser = this.UserList[0];
                }
            }
        }

        /// <summary>
        /// Create the workflow model based on the workflow ID.
        /// </summary>
        private void CreateWorkflowModel()
        {
            this.Workflow = this.workflowManagerService.GetWorkflow(this.workflowId);
            ModelFactory modelFactory = new ModelFactory(null, null);

            this.WorkflowModel = new TridentWorkflowModel(this.Workflow, modelFactory);
        }

        /// <summary>
        /// Updates the workflow model.
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        private void UpdateWorkflowModel(ActivityInstanceParameterCollection<ActivityInstance> parameters)
        {
            if (null != parameters)
            {
                foreach (ActivityInstanceParameter parameter in parameters)
                {
                    BaseModel baseActivity = this.WorkflowModel.ModelsHashtable[parameter.ParameterAssignment.Instance.Name] as BaseModel;

                    if (null != baseActivity)
                    {
                        ParameterDescriptionModel parameterDescriptionModel =
                            baseActivity.InputParameters.FirstOrDefault(p => p.Name.Equals(parameter.ParameterAssignment.ActivityParameter.Name));

                        if (null != parameterDescriptionModel)
                        {
                            parameterDescriptionModel.Value = parameter.ParamValue;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Saves the schedule in registry.
        /// </summary>
        private void SaveScheduleInRegistry()
        {
            try
            {
                // Check if all values are entered in the property pane.
                if (!this.PropertiesValid)
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(ManagementStudioResourceManager.GetString("ReqPropertiesNotEnteredError"));
                    return;
                }

                if ((this.isExpireEnabled && this.EndScheduleTime < this.StartScheduleTime))
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(ManagementStudioResourceManager.GetString("ScheduleTimeError"));
                    return;
                }

                if (!CheckIfAdditionalParametersValid())
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(ManagementStudioResourceManager.GetString("AdditionalParameterError"));
                    return;
                }

                if (!IsSelectedMachineValid(this.selectedNode))
                {
                    return;
                }

                if (this.StartScheduleTime < DateTime.Now)
                {
                    MessageBoxResult result =
                        TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(ManagementStudioResourceManager.GetString("ScheduleTimeElapsedError"));
                    if (result == MessageBoxResult.No)
                        return;
                }


                Machine machine = this.SelectedMachine;

                WorkflowActivityParametersInfo paramInfo =
                    this.WorkflowModel.CreateWorkflowParameterInfo();

                JobComposer jobComposer = new JobComposer(this.workflowManagerService.CurrentConnection);

                // Save the schedule mode.
                if (this.scheduleMode == ScheduleMode.New)
                {
                    JobDetailModel newJob = new JobDetailModel();
                    newJob.JobName = this.JobName;
                    newJob.WorkflowId = this.workflowId;

                    // Add the additional parameters to the new job details.
                    foreach (AdditionalParametersModel additionalParameter in this.AdditionalParameters)
                    {
                        newJob.AdditionalParameters.Add(additionalParameter);
                    }

                    newJob.IsTemplate = true;
                    newJob.NodeId = ((IObject)this.selectedNode).ID;
                    newJob.WorkflowId = this.WorkflowModel.Id;
                    newJob.ParameterInfo = paramInfo;
                    newJob.Notes = this.Notes;

                    Job registryJob = jobComposer.ExecuteWFOnMachine(newJob, this.selectedNode, this.selectedUser, false);

                    this.scheduleToRegistry =
                        this.workflowManagerService.CreateSchedule(this.JobName, registryJob);

                    if (null == this.scheduleToRegistry)
                    {
                        return;
                    }

                    this.scheduleToRegistry.Status = ScheduleStatus.Pending;
                }
                else
                {
                    this.scheduleToRegistry =
                        this.workflowManagerService.GetSchedule(this.scheduleId);

                    this.scheduleToRegistry.Template.Name = this.JobName;
                    this.scheduleToRegistry.Template.Notes = this.Notes;

                    this.scheduleToRegistry.Template.Machines.Clear();
                    this.scheduleToRegistry.Template.Machines.Add(this.selectedNode);

                    foreach (AdditionalParameter paramToDelete in this.scheduleToRegistry.Template.AdditionalParameters)
                    {
                        paramToDelete.Delete();
                    }

                    foreach (AdditionalParametersModel additionalParameter in this.AdditionalParameters)
                    {
                        AdditionalParameter regAdditionalParameter = AdditionalParameter.Create(scheduleToRegistry.Template,
                            this.workflowManagerService.CurrentConnection);

                        regAdditionalParameter.Domain = additionalParameter.Domain;
                        regAdditionalParameter.ParamName = additionalParameter.Parameter;
                        regAdditionalParameter.ParamValue = additionalParameter.ParameterValue;
                        regAdditionalParameter.Save();
                    }


                    this.scheduleToRegistry.Template.ScheduledBy = this.SelectedUser;
                    UpdateJobRequiredParameters(this.scheduleToRegistry.Template, paramInfo);

                    this.scheduleToRegistry.Template.Save();
                    this.scheduleToRegistry.Status =
                        (ScheduleMode.Delete == this.scheduleMode) ? ScheduleStatus.Cancelled : ScheduleStatus.UpdatePending;
                }

                // Save the schedule start and end time.
                this.scheduleToRegistry.StartTime = this.StartScheduleTime;

                if (!this.IsExpireEnabled)
                {
                    this.EndScheduleTime = DateTime.Now.AddYears(1);
                }

                this.scheduleToRegistry.StopTime = this.EndScheduleTime;
                this.scheduleToRegistry.IsExpireEnable = this.IsExpireEnabled;

                // Save the schedule recurrence.
                if (this.WeeklyRecurrence)
                {
                    // Get the selected days.
                    DaysOfWeeks days = DaysOfWeeks.None;
                    if (this.IsMonday)
                        days = days | DaysOfWeeks.Monday;
                    if (this.IsTuesday)
                        days = days | DaysOfWeeks.Tuesday;
                    if (this.IsWednesday)
                        days = days | DaysOfWeeks.Wednesday;
                    if (this.IsThursday)
                        days = days | DaysOfWeeks.Thursday;
                    if (this.IsFriday)
                        days = days | DaysOfWeeks.Friday;
                    if (this.IsSaturday)
                        days = days | DaysOfWeeks.Saturday;
                    if (this.IsSunday)
                        days = days | DaysOfWeeks.Sunday;

                    this.SelectedDays = (int)days;

                    this.scheduleToRegistry.Recurrence = Convert.ToInt32(this.RecurInWeeks, CultureInfo.CurrentUICulture);
                    this.scheduleToRegistry.Schedule = ScheduleType.Weekly;

                    if (this.IsExpireEnabled)
                    {
                        // Save this description if expires enables.
                        this.scheduleToRegistry.Description = ManagementStudioResourceManager.GetString(
                            "WeeklyScheduleDescriptionIfExpires",
                            this.scheduleToRegistry.StartTime.ToString("hh:mm:ss tt", CultureInfo.CurrentUICulture),
                            days.ToString(),
                            this.scheduleToRegistry.Recurrence.ToString(CultureInfo.CurrentUICulture),
                            this.scheduleToRegistry.StartTime.ToString("dd/MM/yyyy", CultureInfo.CurrentUICulture),
                            this.scheduleToRegistry.StopTime.ToString("dd/MM/yyyy hh:mm tt", CultureInfo.CurrentUICulture));
                    }
                    else
                    {
                        this.scheduleToRegistry.Description = ManagementStudioResourceManager.GetString(
                            "WeeklyScheduleDescription",
                            this.scheduleToRegistry.StartTime.ToString("hh:mm:ss tt", CultureInfo.CurrentUICulture),
                            days.ToString(),
                            this.scheduleToRegistry.Recurrence.ToString(CultureInfo.CurrentUICulture),
                            this.scheduleToRegistry.StartTime.ToString("dd/MM/yyyy", CultureInfo.CurrentUICulture));
                    }
                }
                else if (this.DailyRecurrence)
                {
                    this.scheduleToRegistry.Recurrence = Convert.ToInt32(this.RecurInDays, CultureInfo.CurrentUICulture);
                    this.scheduleToRegistry.Schedule = ScheduleType.Daily;

                    if (this.IsExpireEnabled)
                    {
                        // Save this description if expires enables.
                        this.scheduleToRegistry.Description = ManagementStudioResourceManager.GetString(
                            "DailyScheduleDescriptionIfExpires",
                            this.scheduleToRegistry.StartTime.ToString("hh:mm:ss tt", CultureInfo.CurrentUICulture),
                            this.scheduleToRegistry.Recurrence.ToString(CultureInfo.CurrentUICulture),
                            this.scheduleToRegistry.StopTime.ToString("dd/MM/yyyy hh:mm tt", CultureInfo.CurrentUICulture));
                    }
                    else
                    {
                        // Save this description if expires not set.
                        this.scheduleToRegistry.Description = ManagementStudioResourceManager.GetString(
                            "DailyScheduleDescription",
                            this.scheduleToRegistry.StartTime.ToString("hh:mm:ss tt", CultureInfo.CurrentUICulture),
                            this.scheduleToRegistry.Recurrence.ToString(CultureInfo.CurrentUICulture));
                    }
                }
                else
                {
                    // Save this description.
                    this.scheduleToRegistry.Schedule = ScheduleType.OneTime;
                    this.scheduleToRegistry.Description = ManagementStudioResourceManager.GetString(
                            "OneTimeScheduleDescription",
                            this.scheduleToRegistry.StartTime.ToString("hh:mm:ss tt", CultureInfo.CurrentUICulture));
                }

                // Save Jobname and additional parametrs.
                this.scheduleToRegistry.Name =
                    (!string.IsNullOrEmpty(this.JobName)) ?
                    this.JobName : this.WorkflowModel.Name + " - " + DateTime.Now.ToString();

                this.scheduleToRegistry.ScheduledBy = this.SelectedUser;

                this.scheduleToRegistry.Days = this.SelectedDays;
                this.scheduleToRegistry.Save();

                this.IsScheduleRegistryModified = true;
                this.view.Close();

            }
            catch (TridentCustomException tridentException)
            {                
                TridentErrorHandler.HandleUIException(tridentException);
                TridentErrorHandler.HandleTridentCustomExceptionInUI(tridentException);                
                this.view.Close();
            }
            catch (Exception exception)
            {
                TridentErrorHandler.ProcessKnownExceptionsInBusinessLogic(exception);
                TridentErrorHandler.ThrowTridentCustomException(
                    TridentErrorConstants.ErrorNumber1000000023,
                    ManagementStudioResourceManager.GetString("ScheduleSaveError"));
                this.view.Close();
            }

        }

        /// <summary>
        /// Determines whether the selected machine is valid for the job to be executed
        /// </summary>
        /// <param name="selectedMachine"></param>
        /// <returns></returns>
        private bool IsSelectedMachineValid(Machine selectedMachine)
        {
            bool isValid = false;
            if (selectedMachine != null)
            {
                Machine machine = Machine.Load((selectedMachine as IObject).ID, this.workflowManagerService.CurrentConnection);
                if (!machine.IsDeleted && machine.Availability == ExecutorAvailabiltiy.Online)
                {
                    isValid = true;
                }
                else
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(ManagementStudioResourceManager.GetString("InvalidNodeSelectedToExecuteJob"));
                }
            }
            else
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ManagementStudioResourceManager.GetString("MachineCannotBeEmpty"));
            }
            return isValid;
        }

        /// <summary>
        /// Checks if any of the day is selected.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if [is any day selected]; otherwise, <c>false</c>.
        /// </returns>
        private void ValidateSeletctedWeekdays()
        {
            if (this.WeeklyRecurrence)
            {
                this.IsValidData = this.IsSunday | this.IsMonday |
                    this.IsTuesday | this.IsWednesday |
                    this.IsThursday | this.IsFriday |
                    this.IsSaturday;
            }
            else
            {
                this.IsValidData = true;
            }
        }

        /// <summary>
        /// Create the required job param entries for the workflow in the required params table.
        /// </summary>
        /// <param name="job">The job instance.</param>
        /// <param name="workflowParamInfo">The workflow info.</param>
        private void UpdateJobRequiredParameters(Job job, WorkflowActivityParametersInfo workflowParamInfo)
        {
            Collection<ParameterAssignment> assignedParameters = this.workflowManagerService.GetAllParameterAssignments(job.ActivityInstances[0].Activity.BaseSequence);

            foreach (ActivityInstanceParameter parameter in job.ActivityInstances[0].ActivityInstanceParameters)
            {
                parameter.Delete();
            }

            foreach (ActivityParameterInfo activityParameterInfo in workflowParamInfo.Parameters)
            {
                ParameterAssignment reqAssignment = assignedParameters.FirstOrDefault(
                    p => p.Instance.Name.Equals(activityParameterInfo.ActivityUniqueName, StringComparison.Ordinal) &&
                    p.ActivityParameter.Name.Equals(activityParameterInfo.Name, StringComparison.Ordinal));

                if (reqAssignment != null)
                {
                    this.workflowManagerService.CreateActivityInstanceParameter(
                    activityParameterInfo.Value,
                    job.ActivityInstances[0],
                    reqAssignment);
                }
            }
        }

        /// <summary>
        /// Check if the additional parameters have valid values/ names.
        /// </summary>
        /// <returns>True if the additional parameters are valid. False otherwise.</returns>
        private bool CheckIfAdditionalParametersValid()
        {
            AdditionalParametersModel invalidModel = this.AdditionalParameters.FirstOrDefault(p => (string.IsNullOrEmpty(p.Parameter) || string.IsNullOrEmpty(p.ParameterValue)));
            return (invalidModel == null);
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Fires the property changed event.
        /// </summary>
        /// <param name="propertyName">Name of the property which has been modified</param>
        private void OnNotifyPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}
